#include "connectPool.h"
#include <fstream>
#include <iostream>
#include <jsoncpp/json/json.h>
#include <pthread.h>

using namespace std;

// 提供一个静态方法，让外部调用唯一实例
ConnectPool *ConnectPool::getConnectPool()
{
    static ConnectPool pool;
    return &pool;
}

// 从连接池中取出一个MySql连接
std::shared_ptr<MySqlDataBase> ConnectPool::getConnection()
{
    // 得到的MySqlDataBase对象一定是已经连接上服务器的
    /*
        如何得到一个连接？
            1. 判断连接池中是否有可用的连接
            2. 如果没有，就条件等待
    */

    // 加锁
    std::unique_lock<mutex> locker(m_mutex);
    while (m_connectQueue.empty())
    {
        // 等待条件变量
        if (std::cv_status::timeout == m_cond_consume.wait_for(locker, std::chrono::milliseconds(500)));
        {
            // 继续等待
            if (m_connectQueue.empty())
            {
                continue;
            }
        }
    }

    // 程序到了这个地方：数据库连接池有可用的连接
    std::shared_ptr<MySqlDataBase> connPtr(m_connectQueue.front(), [this](MySqlDataBase *conn) {
        // 共享资源
        m_mutex.lock();
        // 刷新时间
        conn->refreshAliveTime();
        // 添加到连接池中
        m_connectQueue.push(conn);
        // 解锁
        m_mutex.unlock();
    });

    // 取出队头元素
    m_connectQueue.pop();

    // 唤醒生产者
    m_cond_produce.notify_all();

    return connPtr;
}

// 解析配置文件
bool ConnectPool::parseConfigFile(const std::string &path)
{
    ifstream ifs(path);
    if (ifs.is_open() == false)
    {
        cout << "failed to open config file." << endl;
        return false;
    }

    Json::Reader rd;
    Json::Value root;

    if (rd.parse(ifs, root) == false)
    {
        cout << "json parse failed." << endl;
        return false;
    }

    m_hostname = root["hostname"].asString();
    m_username = root["username"].asString();
    m_passwd = root["passwd"].asString();
    m_port = root["port"].asInt();
    m_dbname = root["dbname"].asString();
    m_minSize = root["minSize"].asInt();
    m_maxSize = root["maxSize"].asInt();
    m_maxIdleTime = root["maxIdleTime"].asInt();

    return true;
}

// 添加连接到连接池中
bool ConnectPool::addConnection()
{
    MySqlDataBase *conn = new MySqlDataBase(m_hostname, m_username, m_passwd);

    // 对每一个连接上数据库的连接打上时间戳
    conn->refreshAliveTime();

    // 连接数据库
    conn->connectDB(m_dbname);

    // 添加连接到连接池
    m_connectQueue.push(conn);

    return true;
}

// 生产线程入口（向数据库来凝结吃生产连接句柄）
void ConnectPool::produceConnection()
{
    while (true)
    {
        // unique_lock 智能锁
        unique_lock<mutex> locker(m_mutex);
        // 生产连接
        while (m_connectQueue.size() >= m_minSize)
        {
            // 等待条件变量
            m_cond_produce.wait(locker);
        }

        // 添加连接到连接池
        addConnection();

        // 通知消费者
        m_cond_consume.notify_all();
    }
}

// 回收线程入口（回收数据库连接池的连接）
void ConnectPool::recycleConnection()
{
    while (true)
    {
        // 休眠1s
        this_thread::sleep_for(std::chrono::seconds(1));
        // 加锁
        std::unique_lock<mutex> locker(m_mutex);
        // 连接池的连接句柄数量必须大于最小值
        while (m_connectQueue.size() > m_minSize)
        {
            // 查看队头
            MySqlDataBase * conn = m_connectQueue.front();
            // 判断存活时间是否过长（存活时间 = 当前时间 - 上次操作时间）
            if (conn->getAliveTime() > m_maxIdleTime)
            {
                m_connectQueue.pop();
                delete conn;
                conn = nullptr;
            }
        }
    }
}

// 构造函数私有化（写成单例需要将构造私有）
ConnectPool::ConnectPool()
{
    // 解析JSON配置文件
    if (parseConfigFile("./config.json") == false)
    {
        return;
    }

    // 将连接添加到连接池中
    for (int idx = 0; idx < m_minSize; idx++)
    {
        addConnection();
    }

    // 生产者
    thread produce(&ConnectPool::produceConnection, this);

    // 回收者
    thread recycle(&ConnectPool::recycleConnection, this);

    // 线程分离
    produce.detach();
    recycle.detach();
}

// 析构
ConnectPool::~ConnectPool()
{
    // todo...
}
